﻿//////////////////////////////////////////////////////////////////////////////
//
//  Copyright © 1998-2024 Glodon Company Limited.  All rights reserved.
//
//  Use of this software is subject to the terms of the Glodon license
//  agreement provided at the time of installation or download, or which
//  otherwise accompanies this software in either electronic or hard copy form.  
//
//////////////////////////////////////////////////////////////////////////////
#pragma once

#include "GcmpUiBase.h"
#include "GnufImpl.h"

class QObject;

namespace gcmp {

    class GCMP_UIBASE_EXPORT IQtObjectWatcherHandler
    {
    public:
        virtual void onDestroyed(const QObject * const) = 0;
    };

    namespace impl { class GNUF_IMPL_CLASS(QtObjectWatcher); }
    class GCMP_UIBASE_EXPORT QtObjectWatcher final
    {
        GNUF_IMPL_USE(QtObjectWatcher)
        CANNOT_COPY_OR_ASSIGN(QtObjectWatcher)
    public:
        explicit QtObjectWatcher(IQtObjectWatcherHandler *);
        ~QtObjectWatcher() {}

        void watch(QObject *, const bool);
        bool isValid(const QObject *) const;
        void    declareExplicitlyThreadSafe(bool);
        bool didDeclareExplicitlyThreadSafe() const;

        static void destroy(gcmp::OwnerPtr< QObject > &&);
    };

    namespace impl { class QtWeakPtrDataImpl; }
    class GCMP_UIBASE_EXPORT QtWeakPtrData final
    {
        GNUF_IMPL_USE(QtWeakPtrData)
        CANNOT_COPY_OR_ASSIGN(QtWeakPtrData)
    public:
        explicit QtWeakPtrData();
        ~QtWeakPtrData() {}

        bool  isNull() const;
        bool isValid() const;

        void set(QObject *);
        const QObject * get() const;
              QObject * get();
        
        void    declareExplicitlyThreadSafe(bool);
        bool didDeclareExplicitlyThreadSafe() const;

        template <class T>
        const T * getAs() const {
            const QObject * pQtObj(get());
            return (nullptr == pQtObj ? nullptr : static_cast<const T *>(pQtObj));
        }

        template <class T>
        T * getAs() {
            QObject * pQtObj(get());
            return (nullptr == pQtObj ? nullptr : static_cast< T *>(pQtObj));
        }
    };

    /// \brief A QObject can get deleted by its parent QObject, or by the other code. QtAutoPtr
    /// class intends to manage this ambiguous ownership. When QtAutoPtr destructs, delete
    /// immediately the wrapped QObject if it isn't a QWidget. If it's a QWidget, leave it to
    /// the Qt system (calling QObject::deleteLater()), no matter whether its parent QObject
    /// is nullptr.
    template < class TQtObject >
    class QtAutoPtr final : public IQtObjectWatcherHandler
    {
        static_assert(!std::is_pointer< TQtObject >::value,
            "QtAutoPtr's template type must not be a pointer type!");

    public:
        QtAutoPtr()                                    : m_watcher(this) {}
        QtAutoPtr(TQtObject * ptr)                     : m_watcher(this) { reset(ptr); }
        QtAutoPtr(QtAutoPtr< TQtObject > &) = delete;
        QtAutoPtr(QtAutoPtr< TQtObject > && that)      : m_watcher(this) { reset(TransferOwnership(that)); }
        QtAutoPtr(gcmp::OwnerPtr< TQtObject > && optr) : m_watcher(this) { reset(TransferOwnership(optr)); }
        ~QtAutoPtr()                                                     { clear(); }

    public:
        const TQtObject * data() const { return m_optr.get(); }
        ////////////////////////////////////////////////////////////////////////////////////////////
        bool    isNull() const { return (nullptr == m_optr.get()); }
        bool   isValid() const { return (!isNull() && m_watcher.isValid(m_optr.get())); }
        operator  bool() const { return  isValid(); }
        bool operator!() const { return !isValid(); }
        ////////////////////////////////////////////////////////////////////////////////////////////
        const TQtObject * get()         const { return (isValid() ? m_optr.get() : nullptr); }
              TQtObject * get()               { return (isValid() ? m_optr.get() : nullptr); }
        const TQtObject * operator->()  const { return get(); }
              TQtObject * operator->()        { return get(); }
        operator const TQtObject * ()   const { return get(); }
        ////////////////////////////////////////////////////////////////////////////////////////////
        TQtObject & operator*()
        {
            DBG_WARN_UNLESS(isValid(), L"Invalid!", L"GMEP", L"2022-06-06");
            return *get();
        }
        const TQtObject & operator*() const
        {
            DBG_WARN_UNLESS(isValid(), L"Invalid!", L"GMEP", L"2022-06-06");
            return *get();
        }
        ////////////////////////////////////////////////////////////////////////////////////////////
        QtAutoPtr & operator=(TQtObject * pQtObject)
        {
            reset(pQtObject);
            return *this;
        }
        QtAutoPtr & operator=(QtAutoPtr< TQtObject >  &) = delete;
        QtAutoPtr & operator=(QtAutoPtr< TQtObject > && that)
        {
            reset(TransferOwnership(that));
            return *this;
        }
        QtAutoPtr & operator=(gcmp::OwnerPtr< TQtObject > && opQtObject)
        {
            reset(TransferOwnership(opQtObject));
            return *this;
        }
        ////////////////////////////////////////////////////////////////////////////////////////////
        void reset() { clear(); }
        void reset(TQtObject * ptr)
        {
            DBG_WARN_AND_RETURN_VOID_UNLESS(nullptr == data() || ptr != data(),
                L"Failed to reset! Fix it, soon!", L"GMEP", L"2023-10-25");

            clear();

            if (nullptr == ptr)
                return;

            m_optr.reset(ptr);                      // own it
            m_watcher.watch(m_optr.get(), true);    // watch it
        }
        void reset(QtAutoPtr< TQtObject > && that)
        {
            DBG_WARN_AND_RETURN_VOID_UNLESS(nullptr == data() || that.data() != data(),
                L"Failed to reset! Fix it, soon!", L"GMEP", L"2023-10-25");

            clear();

            if (that.isNull())
                return;

            m_optr.reset(that.releaseOwnership());  // accept the ownership
            m_watcher.watch(m_optr.get(), true);    // watch it
            m_watcher.declareExplicitlyThreadSafe(  // accept Thread-Safe
                that.m_watcher.didDeclareExplicitlyThreadSafe());
        }
        void reset(gcmp::OwnerPtr< TQtObject > && optr)
        {
            DBG_WARN_AND_RETURN_VOID_UNLESS(nullptr == data() || optr.get() != data(),
                L"Failed to reset! Fix it, soon!", L"GMEP", L"2023-10-25");

            clear();

            if (nullptr == optr.get())
                return;

            m_optr = TransferOwnership(optr);       // own it
            m_watcher.watch(m_optr.get(), true);    // watch it
        }
        ////////////////////////////////////////////////////////////////////////////////////////////
        void clear()
        {
            m_watcher.declareExplicitlyThreadSafe(false);

            if (isNull())
                return;

            m_watcher.watch(m_optr.get(), false);                       // Don't watch it, any more!
            QtObjectWatcher::destroy(TransferOwnership(m_optr));        // Destroy!!!
            DBG_WARN_UNLESS(nullptr == m_optr.get(),
                L"The watched QtObject isn't destroyed, yet!", L"GMEP", L"2022-06-30");
        }
        TQtObject * release() { return releaseOwnership(); } // for Downward Compatibility
        TQtObject * releaseOwnership()
        {
            m_watcher.declareExplicitlyThreadSafe(false);

            if (isNull())
                return nullptr;

            m_watcher.watch(m_optr.get(), false);                       // Don't watch it, any more!
            TQtObject * ptr = TRANSFER_OWNERSHIP_TO_THIRD_PARTY(m_optr);// Release!!!
            DBG_WARN_UNLESS(nullptr == m_optr.get(),
                L"The watched QtObject isn't released, yet!", L"GMEP", L"2022-06-30");

            return ptr;
        }

        ////////////////////////////////////////////////////////////////////////////////////////////
        // By default, QtAutoPtr::didDeclareExplicitlyThreadSafe() returns false. This means that,
        // even though the TQtObject isn't null, if it's a QWidget, QtAutoPtr::get() returns nullptr
        // for Thread-Safe, unless that declareExplicitlyThreadSafe() was called before. Please call
        // cautiously declareExplicitlyThreadSafe(). -frank 2023.08.16.
        void    declareExplicitlyThreadSafe()       {        m_watcher.declareExplicitlyThreadSafe(true); }
        bool didDeclareExplicitlyThreadSafe() const { return m_watcher.didDeclareExplicitlyThreadSafe();  }
        
        void weakPtrIsApplyingForData(QtWeakPtrData & data)
        {
            DBG_WARN_AND_RETURN_VOID_UNLESS(data.isNull(),
                L"Nonnull data!", L"GMEP", L"2023-10-25");
            data.set(m_optr.get());
        }

    private:
        // Inherits from IQtObjectWatcherHandler...
        virtual void onDestroyed(const QObject * const pDestroyed) override
        {
            DBG_WARN_AND_RETURN_VOID_UNLESS(nullptr != pDestroyed && nullptr != m_optr.get(),
                L"Null pDestroyed or m_opOwner!", L"GMEP", L"2022-06-30");
            const void * pWatched = static_cast<const void *>(m_optr.get());
            DBG_WARN_AND_RETURN_VOID_UNLESS(pDestroyed == pWatched,
                L"The pDestroyed isn't the pWatched!", L"GMEP", L"2022-06-30");
            TRANSFER_OWNERSHIP_TO_THIRD_PARTY(m_optr); // Release!!!
        }

    private:
        QtObjectWatcher             m_watcher;
        gcmp::OwnerPtr< TQtObject > m_optr;
    };
    
    /// \brief QtWeakPtr class intends to guard the QObject. When QtWeakPtr destructs, don't delete
    /// the guarded QObject.
    template < class TQtObject >
    class QtWeakPtr final
    {
        static_assert(!std::is_pointer< TQtObject >::value,
            "QtWeakPtr's template type must not be a pointer type!");

    public:
        QtWeakPtr()                              {}
        QtWeakPtr(TQtObject * ptr)               { reset(ptr); }
        QtWeakPtr(QtWeakPtr< TQtObject > & that) { reset(that); }
        QtWeakPtr(QtWeakPtr< TQtObject > &&) = delete;
        QtWeakPtr(QtAutoPtr< TQtObject > & aptr) { reset(aptr); }
        ~QtWeakPtr()                             { clear(); }
        
    public:
        const TQtObject * data() const { return m_data.getAs<TQtObject>(); }
        ////////////////////////////////////////////////////////////////////////////////////////////
        bool    isNull() const { return m_data.isNull(); }
        bool   isValid() const { return m_data.isValid(); }
        operator  bool() const { return  isValid(); }
        bool operator!() const { return !isValid(); }
        ////////////////////////////////////////////////////////////////////////////////////////////
        const TQtObject * get()         const { return (isValid() ? m_data.getAs<TQtObject>() : nullptr); }
              TQtObject * get()               { return (isValid() ? m_data.getAs<TQtObject>() : nullptr); }
        const TQtObject * operator->()  const { return get(); }
              TQtObject * operator->()        { return get(); }
        operator const TQtObject * ()   const { return get(); }
        ////////////////////////////////////////////////////////////////////////////////////////////
        TQtObject & operator*()
        {
            DBG_WARN_UNLESS(isValid(), L"Invalid!", L"GMEP", L"2023-10-25");
            return *get();
        }        
        const TQtObject & operator*() const
        {            
            DBG_WARN_UNLESS(isValid(), L"Invalid!", L"GMEP", L"2023-10-25");
            return *get();
        }
        ////////////////////////////////////////////////////////////////////////////////////////////
        QtWeakPtr & operator=(TQtObject * ptr)
        {
            reset(ptr);
            return *this;
        }
        QtWeakPtr & operator=(QtAutoPtr< TQtObject > & aptr)
        {
            reset(aptr);
            return *this;
        }
        QtWeakPtr & operator=(QtWeakPtr< TQtObject > & that)
        {
            reset(that);
            return *this;
        }
        QtWeakPtr & operator=(QtWeakPtr< TQtObject > &&) = delete;
        ////////////////////////////////////////////////////////////////////////////////////////////
        void reset() { clear(); }
        void reset(TQtObject * ptr)
        {
            clear();
            
            if (nullptr == ptr)
                return;
            
            m_data.set(ptr);                        // cache it
        }
        void reset(QtAutoPtr< TQtObject > & aptr)
        {
            clear();
            
            if (nullptr == aptr.data())
                return;
            
            aptr.weakPtrIsApplyingForData(m_data);  // cache it
            m_data.declareExplicitlyThreadSafe(     // accept Thread-Safe
                aptr.didDeclareExplicitlyThreadSafe());
        }
        void reset(QtWeakPtr< TQtObject > & that)
        {
            clear();
            
            if (nullptr == that.data())
                return;
            
            m_data.set(that.m_data.get());          // cache it
            m_data.declareExplicitlyThreadSafe(     // accept Thread-Safe
                that.didDeclareExplicitlyThreadSafe());
        }
        ////////////////////////////////////////////////////////////////////////////////////////////
        void clear()
        {
            m_data.declareExplicitlyThreadSafe(false);
            
            if (isNull())
                return;
            
            m_data.set(nullptr);                    // Nullify!!!
        }
        
        ////////////////////////////////////////////////////////////////////////////////////////////
        // By default, QtAutoPtr::didDeclareExplicitlyThreadSafe() returns false. This means that,
        // even though the TQtObject isn't null, if it's a QWidget, QtAutoPtr::get() returns nullptr
        // for Thread-Safe, unless that declareExplicitlyThreadSafe() was called before. Please call
        // cautiously declareExplicitlyThreadSafe(). -frank 2023.08.16.
        void    declareExplicitlyThreadSafe()       {        m_data.declareExplicitlyThreadSafe(true); }
        bool didDeclareExplicitlyThreadSafe() const { return m_data.didDeclareExplicitlyThreadSafe();  }
        
    private:
        QtWeakPtrData m_data;
    };
}

// QtAutoPtr ///////////////////////////////////////////////////////////////////////////////////////
template <class TQtObject>
inline bool operator==(const TQtObject * ptr, const gcmp::QtAutoPtr< TQtObject > & aptr)  { return ptr == aptr.data(); }
template <class TQtObject>
inline bool operator==(      TQtObject * ptr, const gcmp::QtAutoPtr< TQtObject > & aptr)  { return ptr == aptr.data(); }
template <class TQtObject>
inline bool operator!=(const TQtObject * ptr, const gcmp::QtAutoPtr< TQtObject > & aptr)  { return ptr != aptr.data(); }
template <class TQtObject>
inline bool operator!=(      TQtObject * ptr, const gcmp::QtAutoPtr< TQtObject > & aptr)  { return ptr != aptr.data(); }
template <class TQtObject>
inline bool operator==(const gcmp::QtAutoPtr< TQtObject > & aptr, const TQtObject * ptr)  { return aptr.data() == ptr; }
template <class TQtObject>
inline bool operator==(const gcmp::QtAutoPtr< TQtObject > & aptr,       TQtObject * ptr)  { return aptr.data() == ptr; }
template <class TQtObject>
inline bool operator!=(const gcmp::QtAutoPtr< TQtObject > & aptr, const TQtObject * ptr)  { return aptr.data() != ptr; }
template <class TQtObject>
inline bool operator!=(const gcmp::QtAutoPtr< TQtObject > & aptr,       TQtObject * ptr)  { return aptr.data() != ptr; }
template <class TQtObject>
inline bool operator==(const gcmp::QtAutoPtr< TQtObject > & aptr1, const gcmp::QtAutoPtr< TQtObject > & aptr2)  { return aptr1.data() == aptr2.data(); }
template <class TQtObject>
inline bool operator!=(const gcmp::QtAutoPtr< TQtObject > & aptr1, const gcmp::QtAutoPtr< TQtObject > & aptr2)  { return aptr1.data() != aptr2.data(); }

// QtWeakPtr ///////////////////////////////////////////////////////////////////////////////////////
template <class TQtObject>
inline bool operator==(const TQtObject * ptr, const gcmp::QtWeakPtr< TQtObject > & wptr)  { return ptr == wptr.data(); }
template <class TQtObject>
inline bool operator==(      TQtObject * ptr, const gcmp::QtWeakPtr< TQtObject > & wptr)  { return ptr == wptr.data(); }
template <class TQtObject>
inline bool operator!=(const TQtObject * ptr, const gcmp::QtWeakPtr< TQtObject > & wptr)  { return ptr != wptr.data(); }
template <class TQtObject>
inline bool operator!=(      TQtObject * ptr, const gcmp::QtWeakPtr< TQtObject > & wptr)  { return ptr != wptr.data(); }
template <class TQtObject>
inline bool operator==(const gcmp::QtWeakPtr< TQtObject > & wptr, const TQtObject * ptr)  { return wptr.data() == ptr; }
template <class TQtObject>
inline bool operator==(const gcmp::QtWeakPtr< TQtObject > & wptr,       TQtObject * ptr)  { return wptr.data() == ptr; }
template <class TQtObject>
inline bool operator!=(const gcmp::QtWeakPtr< TQtObject > & wptr, const TQtObject * ptr)  { return wptr.data() != ptr; }
template <class TQtObject>
inline bool operator!=(const gcmp::QtWeakPtr< TQtObject > & wptr,       TQtObject * ptr)  { return wptr.data() != ptr; }
template <class TQtObject>
inline bool operator==(const gcmp::QtWeakPtr< TQtObject > & wptr1, const gcmp::QtWeakPtr< TQtObject > & wptr2)  { return wptr1.data() == wptr2.data(); }
template <class TQtObject>
inline bool operator!=(const gcmp::QtWeakPtr< TQtObject > & wptr1, const gcmp::QtWeakPtr< TQtObject > & wptr2)  { return wptr1.data() != wptr2.data(); }

// QtAutoPtr & QtWeakPtr ///////////////////////////////////////////////////////////////////////////
template <class TQtObject>
inline bool operator==(const gcmp::QtWeakPtr< TQtObject > & wptr, const gcmp::QtAutoPtr< TQtObject > & aptr)    { return  wptr.data() == aptr.data(); }
template <class TQtObject>
inline bool operator!=(const gcmp::QtWeakPtr< TQtObject > & wptr, const gcmp::QtAutoPtr< TQtObject > & aptr)    { return  wptr.data() != aptr.data(); }
template <class TQtObject>
inline bool operator==(const gcmp::QtAutoPtr< TQtObject > & aptr, const gcmp::QtWeakPtr< TQtObject > & wptr)    { return  aptr.data() == wptr.data(); }
template <class TQtObject>
inline bool operator!=(const gcmp::QtAutoPtr< TQtObject > & aptr, const gcmp::QtWeakPtr< TQtObject > & wptr)    { return  aptr.data() != wptr.data(); }